
#include <system_log.h>
#include <ff_plugin.h>

extern FFSdkUnity::Plugin g_plugin;

extern "C" {

// Special C# function for getting "OnRenderEvent" function pointer. This function will be called later by Unity engine
// in the rendering thread.
// Please refer for more details: https://github.com/Unity-Technologies/NativeRenderingPlugin
//
// Look at the PluginSource/source/RenderingPlugin.cpp,
// extern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API GetRenderEventFunc()
//
// and UnityProject/Assets/UseRenderingPlugin.cs, GL.IssuePluginEvent(GetRenderEventFunc(), 1);

enum FFVkRenderEventId : int
{
    FRAME_FLOW_ACTIVATE_EVENT_ID = 776,
    FRAME_FLOW_PREDICT_EVENT_ID = 778,
    FRAME_FLOW_PROVIDE_DATA_EVENT_ID = 781
};

#define VK_WRAPPER g_plugin.GetWrapperVK()
#define VK_ON_RENDER_EVENT OnRenderEventVK

FFBool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API FrameFlowVK_IsActive() noexcept
{
    return VK_WRAPPER.OnIsActive();
}

FFResult UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API FrameFlowVK_Activate()
{
    return VK_WRAPPER.OnActivate();
}

FFResult UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API FrameFlowVK_Deactivate()
{
    return VK_WRAPPER.OnDeactivate();
}

FFResult UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API FrameFlowVK_SetPredictionAlgorithm(
    FFPredictionAlgorithm algorithm, bool extrapolation)
{
    FFLOGI("Unity_FrameFlowVK_SetPredictionAlgorithm start");
    return VK_WRAPPER.OnSetPredictionAlgorithm(algorithm, extrapolation);
}

FFResult UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API FrameFlowVK_SetInputDescription(
    FFSdkUnity::Vulkan::FFUnityInputDescription const *unity_desc)
{
    return VK_WRAPPER.OnSetInputDescription(unity_desc);
}

FFResult UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API FrameFlowVK_SetStencilRemapInfo(
    FFSdkUnity::Vulkan::FFUnityRemapTable const *unity_table)
{
    return VK_WRAPPER.OnSetStencilRemapInfo(unity_table);
}

static void UNITY_INTERFACE_API VK_ON_RENDER_EVENT(int eventID, void *data)
{
    FFResult result = FF_SUCCESS;
    switch (eventID) {
        case FFVkRenderEventId::FRAME_FLOW_ACTIVATE_EVENT_ID: {
            result = VK_WRAPPER.OnActivate();
            break;
        }
        case FFVkRenderEventId::FRAME_FLOW_PREDICT_EVENT_ID: {
            result = VK_WRAPPER.OnDrawFrame(data);
            break;
        }
        case FFVkRenderEventId::FRAME_FLOW_PROVIDE_DATA_EVENT_ID: {
            auto unity_data = static_cast<FFSdkUnity::Vulkan::FFUnityFrameData const *>(data);
            result = VK_WRAPPER.OnProvidePerFrameData(unity_data);
            break;
        }
        default: {
            FFLOGD("OnRenderEvent - Unexpected event %d", eventID);
            break;
        }
    }

    if (result != FF_SUCCESS) {
        FFLOGW("OnRenderEvent - Event call %d returned: %d", eventID, static_cast<int>(result));
    }
}

UnityRenderingEventAndData UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API FrameFlowVK_GetRenderEventFunc()
{
    return &VK_ON_RENDER_EVENT;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

} // extern "C"
